#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <memory>
#include <unordered_map>
#include <queue>
#include <cerrno>
#include <cstring>
#include <ctime>
#include <sys/epoll.h>
#include "util.hpp"
#include "easysock.hpp"
#include "easylog.hpp"
#include "epoll.hpp"
#include "task.hpp"
#include "protocol.hpp"

namespace server
{
struct connection;

const int         default_fd    = -1;
const std::string default_ip    = "127.0.0.1";
const uint16_t    default_port  = -1;
const bool        default_mode  = false;
const ssize_t     default_size  = 1024;

const ssize_t     KEEP_LIVE_TIME = 30;

using service_t  = std::function<proto::response(const proto::request&)>;
using callback_t = std::function<void(connection&)>;
using backmgr_t  = std::function<void(int)>;

struct connection
{
    connection(int fd = default_fd, event_type ev = epnil, bool etmode = false, std::string ip = default_ip,
               uint16_t port = default_port)
        : _fd(fd) , _ev(ev) , _etmode(etmode) , _ip(ip) , _port(port), _last_time(time(nullptr))
    {}

    void register_cb(callback_t recver, callback_t sender, callback_t excepter)
    {
        _recver   = recver;
        _sender   = sender;
        _excepter = excepter;
    }

    void update_live_time()
    {
        _last_time = time(nullptr);
    }

    int _fd;
    event_type _ev;
    bool _etmode;

    std::string _ip;
    uint16_t _port;

    std::string _ibuf;
    std::string _obuf;

    callback_t _recver;
    callback_t _sender;
    callback_t _excepter;

    backmgr_t _killer;

    time_t _last_time;
};

class epoll_server : public inet::tcp::server
{
public:
    epoll_server(uint16_t port, service_t service = nullptr, bool etmode = default_mode)
        : server(port), _service(service), _etmode(etmode)
    {}

    ~epoll_server()
    {
        for (auto& kv : _conns) { close(kv.first); }
    }

    void init()
    {
        _ep.create();
        add_conn(_sock, epin);
    }

    void dispatcher()
    {
        int timeout = 1000;

        while (true)
        {
            looper(timeout);
            timeout = checklive();
        }
    }

    void looper(int timeout)
    {
        int n = _ep.wait(timeout);

        for (int i = 0; i < n; i++)
        {
            const uint32_t& events = _ep[i].events;
            const int& fd = _ep[i].data.fd;

            // if (events & everr) events |= (epin | epout);
            if (is_exist(fd) && (events & epin )) _conns[fd]._recver(_conns[fd]);
            if (is_exist(fd) && (events & epout)) _conns[fd]._sender(_conns[fd]);
            if (is_exist(fd) && (events & eperr)) _conns[fd]._excepter(_conns[fd]);
        }
    }

    int checklive()
    {
        time_t curtime = time(nullptr);
        time_t mintime = 0;

        for (auto& kv : _conns)
        {
            if (kv.second._last_time + KEEP_LIVE_TIME > curtime)
                del_conn(kv.first);

            mintime = std::min(mintime, kv.second._last_time);
        }

        return curtime - mintime;
    }

private:
    void acceptor(connection& conn)
    {
        do 
        {
            std::string cip;
            uint16_t cport;
            int newfd = accept(&cip, &cport);

            if (newfd < 0)
                break;

            add_conn(newfd, epin, cip, cport);
            INFO("accept connect [%s:%d] on %d", cip.c_str(), cport, newfd);
        }
        while (_etmode);
    }

    void handler_request(connection& conn)
    {
        while (true)
        {
            std::string reqstr;
            if (proto::parserapack(conn._ibuf, &reqstr) < 0)
                break;

            proto::request req(reqstr);
            proto::response rsp = _service(req);

            conn._obuf += rsp.serialize();
        }

        mod_conn(conn._fd, epio);
    }
    void recver(connection& conn)
    {
        INFO("handler recv event on %d", conn._fd);

        conn.update_live_time();

        do 
        {
            std::string buf;
            ssize_t s = recv(conn._fd, &buf, default_size);

            if (s > 0)
            {
                conn._ibuf += buf;
                handler_request(conn);

                if (!conn._obuf.empty()) conn._sender(conn); // 一般直接写，缓冲区被打满没写完的才交给epoll
            }
            else if (s == 0) 
            {
                conn._excepter(conn);
                break;
            }
            else 
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) break;
                else if (errno == EINTR) continue;
                else conn._excepter(conn);
                break;
            }
        }
        while (conn._etmode);
    }

    void sender(connection& conn)
    {
        INFO("handler send event on %d", conn._fd);

        do 
        {
            ssize_t s = send(conn._fd, conn._obuf);

            if (s > 0)
            {
                conn._obuf.erase(0, s);
                if (conn._obuf.empty()) break;
            }
            else if (s == 0) // 考虑ret=0的情况
            {
                break;
            }
            else 
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK) break; // send buffer is full
                else if (errno == EINTR) continue;
                else 
                {
                    conn._excepter(conn);
                    return; // 防止下面判断越界
                }
            }
        }
        while (conn._etmode);

        if (conn._obuf.empty()) mod_conn(conn._fd, epin); // 如果缓冲区未满，不关闭写入事件，等于是交给epoll处理
    }

    void excepter(connection& conn)
    {
        INFO("handler except event on %d", conn._fd);
        del_conn(conn._fd);
    }

    void add_conn(int fd, event_type ev, const std::string& ip = default_ip, uint16_t port = default_port)
    {
        if (_etmode) util::set_nonblock(fd);

        _conns.insert({fd, connection(fd, ev, _etmode, ip, port)});

        if (fd == _sock)
        {
            _conns[fd].register_cb(std::bind(&epoll_server::acceptor, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            _conns[fd].register_cb(std::bind(&epoll_server::recver,   this, std::placeholders::_1), 
                                   std::bind(&epoll_server::sender,   this, std::placeholders::_1),
                                   std::bind(&epoll_server::excepter, this, std::placeholders::_1));
        }

        if      (ev == epin)  _ep.add_event( fdev_in(fd, _etmode));
        else if (ev == epio)  _ep.add_event( fdev_io(fd, _etmode));
        else if (ev == epout) _ep.add_event(fdev_out(fd, _etmode));
        else if (ev == eperr) _ep.add_event(fdev_err(fd, _etmode));
    }

    void mod_conn(int fd, event_type ev)
    {
        if      (ev == epin)  _ep.mod_event( fdev_in(fd, _etmode));
        else if (ev == epio)  _ep.mod_event( fdev_io(fd, _etmode));
        else if (ev == epout) _ep.mod_event(fdev_out(fd, _etmode));
        else if (ev == eperr) _ep.mod_event(fdev_err(fd, _etmode));
    }

    void del_conn(int fd)
    {
        _ep.del_event(fdev_in(fd)); // epoll_ctl要求fd必须合法，所以先移除事件再close fd
        _conns.erase(fd);
        close(fd);
    }

    bool is_exist(int fd) { return _conns.find(fd) != _conns.end(); }

private:
    epoll _ep;
    bool _etmode;
    service_t _service;
    std::unordered_map<int, connection> _conns;
};

}
